/*
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * Copyright 2011, Blender Foundation.
 */

#include <stdlib.h>

#include "COM_DoubleEdgeMaskOperation.h"
#include "BLI_math.h"
#include "DNA_node_types.h"
#include "MEM_guardedalloc.h"

// this part has been copied from the double edge mask
static void do_adjacentKeepBorders(unsigned int t,
                                   unsigned int rw,
                                   unsigned int *limask,
                                   unsigned int *lomask,
                                   unsigned int *lres,
                                   float *res,
                                   unsigned int *rsize)
{
  int x;
  unsigned int isz = 0;  // inner edge size
  unsigned int osz = 0;  // outer edge size
  unsigned int gsz = 0;  // gradient fill area size
  /* Test the four corners */
  /* upper left corner */
  x = t - rw + 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel underneath, or to the right, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }
  /* upper right corner */
  x = t;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel underneath, or to the left, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }
  /* lower left corner */
  x = 0;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel above, or to the right, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }
  /* lower right corner */
  x = rw - 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel above, or to the left, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }

  /* Test the TOP row of pixels in buffer, except corners */
  for (x = t - 1; x >= (t - rw) + 2; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel to the right, or to the left, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }

  /* Test the BOTTOM row of pixels in buffer, except corners */
  for (x = rw - 2; x; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel to the right, or to the left, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }
  /* Test the LEFT edge of pixels in buffer, except corners */
  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel underneath, or above, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }

  /* Test the RIGHT edge of pixels in buffer, except corners */
  for (x = t - rw; x > rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel underneath, or above, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }

  rsize[0] = isz;  // fill in our return sizes for edges + fill
  rsize[1] = osz;
  rsize[2] = gsz;
}

static void do_adjacentBleedBorders(unsigned int t,
                                    unsigned int rw,
                                    unsigned int *limask,
                                    unsigned int *lomask,
                                    unsigned int *lres,
                                    float *res,
                                    unsigned int *rsize)
{
  int x;
  unsigned int isz = 0;  // inner edge size
  unsigned int osz = 0;  // outer edge size
  unsigned int gsz = 0;  // gradient fill area size
  /* Test the four corners */
  /* upper left corner */
  x = t - rw + 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel underneath, or to the right, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + 1] && lomask[x + 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    if (!lomask[x - rw] ||
        !lomask[x + 1]) {  // test if outer mask is empty underneath or to the right
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* upper right corner */
  x = t;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel underneath, or to the left, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x - 1] && lomask[x - 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    if (!lomask[x - rw] ||
        !lomask[x - 1]) {  // test if outer mask is empty underneath or to the left
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* lower left corner */
  x = 0;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel above, or to the right, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x + 1] && lomask[x + 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {                       // inner mask was empty, test if outer mask is filled
    if (!lomask[x + rw] || !lomask[x + 1]) {  // test if outer mask is empty above or to the right
      osz++;                                  // increment outer edge size
      lres[x] = 3;                            // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* lower right corner */
  x = rw - 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if pixel above, or to the left, are empty in the inner mask,
    // but filled in the outer mask
    if ((!limask[x + rw] && lomask[x + rw]) || (!limask[x - 1] && lomask[x - 1])) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {                       // inner mask was empty, test if outer mask is filled
    if (!lomask[x + rw] || !lomask[x - 1]) {  // test if outer mask is empty above or to the left
      osz++;                                  // increment outer edge size
      lres[x] = 3;                            // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* Test the TOP row of pixels in buffer, except corners */
  for (x = t - 1; x >= (t - rw) + 2; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel to the left, or to the right, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - 1] ||
          !lomask[x + 1]) {  // test if outer mask is empty to the left or to the right
        osz++;               // increment outer edge size
        lres[x] = 3;         // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }

  /* Test the BOTTOM row of pixels in buffer, except corners */
  for (x = rw - 2; x; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel to the left, or to the right, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - 1] && lomask[x - 1]) || (!limask[x + 1] && lomask[x + 1])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - 1] ||
          !lomask[x + 1]) {  // test if outer mask is empty to the left or to the right
        osz++;               // increment outer edge size
        lres[x] = 3;         // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }
  /* Test the LEFT edge of pixels in buffer, except corners */
  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel underneath, or above, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - rw] || !lomask[x + rw]) {  // test if outer mask is empty underneath or above
        osz++;                                   // increment outer edge size
        lres[x] = 3;                             // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }

  /* Test the RIGHT edge of pixels in buffer, except corners */
  for (x = t - rw; x > rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if pixel underneath, or above, are empty in the inner mask,
      // but filled in the outer mask
      if ((!limask[x - rw] && lomask[x - rw]) || (!limask[x + rw] && lomask[x + rw])) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - rw] || !lomask[x + rw]) {  // test if outer mask is empty underneath or above
        osz++;                                   // increment outer edge size
        lres[x] = 3;                             // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }

  rsize[0] = isz;  // fill in our return sizes for edges + fill
  rsize[1] = osz;
  rsize[2] = gsz;
}

static void do_allKeepBorders(unsigned int t,
                              unsigned int rw,
                              unsigned int *limask,
                              unsigned int *lomask,
                              unsigned int *lres,
                              float *res,
                              unsigned int *rsize)
{
  int x;
  unsigned int isz = 0;  // inner edge size
  unsigned int osz = 0;  // outer edge size
  unsigned int gsz = 0;  // gradient fill area size
  /* Test the four corners */
  /* upper left corner */
  x = t - rw + 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if the inner mask is empty underneath or to the right
    if (!limask[x - rw] || !limask[x + 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }
  /* upper right corner */
  x = t;
  // test if inner mask is filled
  if (limask[x]) {
    // test if the inner mask is empty underneath or to the left
    if (!limask[x - rw] || !limask[x - 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }
  /* lower left corner */
  x = 0;
  // test if inner mask is filled
  if (limask[x]) {
    // test if inner mask is empty above or to the right
    if (!limask[x + rw] || !limask[x + 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }
  /* lower right corner */
  x = rw - 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if inner mask is empty above or to the left
    if (!limask[x + rw] || !limask[x - 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    osz++;               // increment outer edge size
    lres[x] = 3;         // flag pixel as outer edge
  }

  /* Test the TOP row of pixels in buffer, except corners */
  for (x = t - 1; x >= (t - rw) + 2; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty to the left or to the right
      if (!limask[x - 1] || !limask[x + 1]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }

  /* Test the BOTTOM row of pixels in buffer, except corners */
  for (x = rw - 2; x; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty to the left or to the right
      if (!limask[x - 1] || !limask[x + 1]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }
  /* Test the LEFT edge of pixels in buffer, except corners */
  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty underneath or above
      if (!limask[x - rw] || !limask[x + rw]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }

  /* Test the RIGHT edge of pixels in buffer, except corners */
  for (x = t - rw; x > rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty underneath or above
      if (!limask[x - rw] || !limask[x + rw]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
  }

  rsize[0] = isz;  // fill in our return sizes for edges + fill
  rsize[1] = osz;
  rsize[2] = gsz;
}

static void do_allBleedBorders(unsigned int t,
                               unsigned int rw,
                               unsigned int *limask,
                               unsigned int *lomask,
                               unsigned int *lres,
                               float *res,
                               unsigned int *rsize)
{
  int x;
  unsigned int isz = 0;  // inner edge size
  unsigned int osz = 0;  // outer edge size
  unsigned int gsz = 0;  // gradient fill area size
  /* Test the four corners */
  /* upper left corner */
  x = t - rw + 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if the inner mask is empty underneath or to the right
    if (!limask[x - rw] || !limask[x + 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    if (!lomask[x - rw] ||
        !lomask[x + 1]) {  // test if outer mask is empty underneath or to the right
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* upper right corner */
  x = t;
  // test if inner mask is filled
  if (limask[x]) {
    // test if the inner mask is empty underneath or to the left
    if (!limask[x - rw] || !limask[x - 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {                       // inner mask was empty, test if outer mask is filled
    if (!lomask[x - rw] || !lomask[x - 1]) {  // test if outer mask is empty above or to the left
      osz++;                                  // increment outer edge size
      lres[x] = 3;                            // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* lower left corner */
  x = 0;
  // test if inner mask is filled
  if (limask[x]) {
    // test if inner mask is empty above or to the right
    if (!limask[x + rw] || !limask[x + 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    if (!lomask[x + rw] ||
        !lomask[x + 1]) {  // test if outer mask is empty underneath or to the right
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* lower right corner */
  x = rw - 1;
  // test if inner mask is filled
  if (limask[x]) {
    // test if inner mask is empty above or to the left
    if (!limask[x + rw] || !limask[x - 1]) {
      isz++;        // increment inner edge size
      lres[x] = 4;  // flag pixel as inner edge
    }
    else {
      res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
    }
  }
  else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
    if (!lomask[x + rw] ||
        !lomask[x - 1]) {  // test if outer mask is empty underneath or to the left
      osz++;               // increment outer edge size
      lres[x] = 3;         // flag pixel as outer edge
    }
    else {
      gsz++;        // increment the gradient pixel count
      lres[x] = 2;  // flag pixel as gradient
    }
  }
  /* Test the TOP row of pixels in buffer, except corners */
  for (x = t - 1; x >= (t - rw) + 2; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty to the left or to the right
      if (!limask[x - 1] || !limask[x + 1]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - 1] ||
          !lomask[x + 1]) {  // test if outer mask is empty to the left or to the right
        osz++;               // increment outer edge size
        lres[x] = 3;         // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }

  /* Test the BOTTOM row of pixels in buffer, except corners */
  for (x = rw - 2; x; x--) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty to the left or to the right
      if (!limask[x - 1] || !limask[x + 1]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - 1] ||
          !lomask[x + 1]) {  // test if outer mask is empty to the left or to the right
        osz++;               // increment outer edge size
        lres[x] = 3;         // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }
  /* Test the LEFT edge of pixels in buffer, except corners */
  for (x = t - (rw << 1) + 1; x >= rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty underneath or above
      if (!limask[x - rw] || !limask[x + rw]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - rw] || !lomask[x + rw]) {  // test if outer mask is empty underneath or above
        osz++;                                   // increment outer edge size
        lres[x] = 3;                             // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }

  /* Test the RIGHT edge of pixels in buffer, except corners */
  for (x = t - rw; x > rw; x -= rw) {
    // test if inner mask is filled
    if (limask[x]) {
      // test if inner mask is empty underneath or above
      if (!limask[x - rw] || !limask[x + rw]) {
        isz++;        // increment inner edge size
        lres[x] = 4;  // flag pixel as inner edge
      }
      else {
        res[x] = 1.0f;  // pixel is just part of inner mask, and it's not an edge
      }
    }
    else if (lomask[x]) {  // inner mask was empty, test if outer mask is filled
      if (!lomask[x - rw] || !lomask[x + rw]) {  // test if outer mask is empty underneath or above
        osz++;                                   // increment outer edge size
        lres[x] = 3;                             // flag pixel as outer edge
      }
      else {
        gsz++;        // increment the gradient pixel count
        lres[x] = 2;  // flag pixel as gradient
      }
    }
  }

  rsize[0] = isz;  // fill in our return sizes for edges + fill
  rsize[1] = osz;
  rsize[2] = gsz;
}

static void do_allEdgeDetection(unsigned int t,
                                unsigned int rw,
                                unsigned int *limask,
                                unsigned int *lomask,
                                unsigned int *lres,
                                float *res,
                                unsigned int *rsize,
                                unsigned int in_isz,
                                unsigned int in_osz,
                                unsigned int in_gsz)
{
  int x;            // x = pixel loop counter
  int a;            // a = pixel loop counter
  int dx;           // dx = delta x
  int pix_prevRow;  // pix_prevRow = pixel one row behind the one we are testing in a loop
  int pix_nextRow;  // pix_nextRow = pixel one row in front of the one we are testing in a loop
  int pix_prevCol;  // pix_prevCol = pixel one column behind the one we are testing in a loop
  int pix_nextCol;  // pix_nextCol = pixel one column in front of the one we are testing in a loop
  /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
  for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) {
    a = x - 2;
    pix_prevRow = a + rw;
    pix_nextRow = a - rw;
    pix_prevCol = a + 1;
    pix_nextCol = a - 1;
    while (a > dx - 2) {
      if (!limask[a]) {   // if the inner mask is empty
        if (lomask[a]) {  // if the outer mask is full
          /*
           * Next we test all 4 directions around the current pixel: next/prev/up/down
           * The test ensures that the outer mask is empty and that the inner mask
           * is also empty. If both conditions are true for any one of the 4 adjacent pixels
           * then the current pixel is counted as being a true outer edge pixel.
           */
          if ((!lomask[pix_nextCol] && !limask[pix_nextCol]) ||
              (!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
              (!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
              (!lomask[pix_prevRow] && !limask[pix_prevRow])) {
            in_osz++;     // increment the outer boundary pixel count
            lres[a] = 3;  // flag pixel as part of outer edge
          }
          else {          // it's not a boundary pixel, but it is a gradient pixel
            in_gsz++;     // increment the gradient pixel count
            lres[a] = 2;  // flag pixel as gradient
          }
        }
      }
      else {
        if (!limask[pix_nextCol] || !limask[pix_prevCol] || !limask[pix_nextRow] ||
            !limask[pix_prevRow]) {
          in_isz++;     // increment the inner boundary pixel count
          lres[a] = 4;  // flag pixel as part of inner edge
        }
        else {
          res[a] = 1.0f;  // pixel is part of inner mask, but not at an edge
        }
      }
      a--;
      pix_prevRow--;
      pix_nextRow--;
      pix_prevCol--;
      pix_nextCol--;
    }
  }

  rsize[0] = in_isz;  // fill in our return sizes for edges + fill
  rsize[1] = in_osz;
  rsize[2] = in_gsz;
}

static void do_adjacentEdgeDetection(unsigned int t,
                                     unsigned int rw,
                                     unsigned int *limask,
                                     unsigned int *lomask,
                                     unsigned int *lres,
                                     float *res,
                                     unsigned int *rsize,
                                     unsigned int in_isz,
                                     unsigned int in_osz,
                                     unsigned int in_gsz)
{
  int x;            // x = pixel loop counter
  int a;            // a = pixel loop counter
  int dx;           // dx = delta x
  int pix_prevRow;  // pix_prevRow = pixel one row behind the one we are testing in a loop
  int pix_nextRow;  // pix_nextRow = pixel one row in front of the one we are testing in a loop
  int pix_prevCol;  // pix_prevCol = pixel one column behind the one we are testing in a loop
  int pix_nextCol;  // pix_nextCol = pixel one column in front of the one we are testing in a loop
  /* Test all rows between the FIRST and LAST rows, excluding left and right edges */
  for (x = (t - rw) + 1, dx = x - (rw - 2); dx > rw; x -= rw, dx -= rw) {
    a = x - 2;
    pix_prevRow = a + rw;
    pix_nextRow = a - rw;
    pix_prevCol = a + 1;
    pix_nextCol = a - 1;
    while (a > dx - 2) {
      if (!limask[a]) {   // if the inner mask is empty
        if (lomask[a]) {  // if the outer mask is full
          /*
           * Next we test all 4 directions around the current pixel: next/prev/up/down
           * The test ensures that the outer mask is empty and that the inner mask
           * is also empty. If both conditions are true for any one of the 4 adjacent pixels
           * then the current pixel is counted as being a true outer edge pixel.
           */
          if ((!lomask[pix_nextCol] && !limask[pix_nextCol]) ||
              (!lomask[pix_prevCol] && !limask[pix_prevCol]) ||
              (!lomask[pix_nextRow] && !limask[pix_nextRow]) ||
              (!lomask[pix_prevRow] && !limask[pix_prevRow])) {
            in_osz++;     // increment the outer boundary pixel count
            lres[a] = 3;  // flag pixel as part of outer edge
          }
          else {          // it's not a boundary pixel, but it is a gradient pixel
            in_gsz++;     // increment the gradient pixel count
            lres[a] = 2;  // flag pixel as gradient
          }
        }
      }
      else {
        if ((!limask[pix_nextCol] && lomask[pix_nextCol]) ||
            (!limask[pix_prevCol] && lomask[pix_prevCol]) ||
            (!limask[pix_nextRow] && lomask[pix_nextRow]) ||
            (!limask[pix_prevRow] && lomask[pix_prevRow])) {
          in_isz++;     // increment the inner boundary pixel count
          lres[a] = 4;  // flag pixel as part of inner edge
        }
        else {
          res[a] = 1.0f;  // pixel is part of inner mask, but not at an edge
        }
      }
      a--;
      pix_prevRow--;  // advance all four "surrounding" pixel pointers
      pix_nextRow--;
      pix_prevCol--;
      pix_nextCol--;
    }
  }

  rsize[0] = in_isz;  // fill in our return sizes for edges + fill
  rsize[1] = in_osz;
  rsize[2] = in_gsz;
}

static void do_createEdgeLocationBuffer(unsigned int t,
                                        unsigned int rw,
                                        unsigned int *lres,
                                        float *res,
                                        unsigned short *gbuf,
                                        unsigned int *innerEdgeOffset,
                                        unsigned int *outerEdgeOffset,
                                        unsigned int isz,
                                        unsigned int gsz)
{
  int x;              // x = pixel loop counter
  int a;              // a = temporary pixel index buffer loop counter
  unsigned int ud;    // ud = unscaled edge distance
  unsigned int dmin;  // dmin = minimum edge distance

  unsigned int rsl;  // long used for finding fast 1.0/sqrt
  unsigned int gradientFillOffset;

  /* For looping inner edge pixel indexes, represents current position from offset. */
  unsigned int innerAccum = 0;
  /* For looping outer edge pixel indexes, represents current position from offset. */
  unsigned int outerAccum = 0;
  /* For looping gradient pixel indexes, represents current position from offset. */
  unsigned int gradientAccum = 0;

  /*  */
  /* clang-format off */
  /*
   * Here we compute the size of buffer needed to hold (row,col) coordinates
   * for each pixel previously determined to be either gradient, inner edge,
   * or outer edge.
   *
   * Allocation is done by requesting 4 bytes "sizeof(int)" per pixel, even
   * though gbuf[] is declared as (unsigned short *) (2 bytes) because we don't
   * store the pixel indexes, we only store x,y location of pixel in buffer.
   *
   * This does make the assumption that x and y can fit in 16 unsigned bits
   * so if Blender starts doing renders greater than 65536 in either direction
   * this will need to allocate gbuf[] as unsigned int *and allocate 8 bytes
   * per flagged pixel.
   *
   * In general, the buffer on-screen:
   *
   * Example:  9 by 9 pixel block
   *
   * . = pixel non-white in both outer and inner mask
   * o = pixel white in outer, but not inner mask, adjacent to "." pixel
   * g = pixel white in outer, but not inner mask, not adjacent to "." pixel
   * i = pixel white in inner mask, adjacent to "g" or "." pixel
   * F = pixel white in inner mask, only adjacent to other pixels white in the inner mask
   *
   *
   * .........   <----- pixel #80
   * ..oooo...
   * .oggggo..
   * .oggiggo.
   * .ogiFigo.
   * .oggiggo.
   * .oggggo..
   * ..oooo...
   * pixel #00 -----> .........
   *
   * gsz = 18   (18 "g" pixels above)
   * isz = 4    (4 "i" pixels above)
   * osz = 18   (18 "o" pixels above)
   *
   *
   * The memory in gbuf[] after filling will look like this:
   *
   * gradientFillOffset (0 pixels)                   innerEdgeOffset (18 pixels)    outerEdgeOffset (22 pixels)
   * /                                               /                              /
   * /                                               /                              /
   * |X   Y   X   Y   X   Y   X   Y   >     <X   Y   X   Y   >     <X   Y   X   Y   X   Y   >     <X   Y   X   Y   | <- (x,y)
   * +-------------------------------->     <---------------->     <------------------------>     <----------------+
   * |0   2   4   6   8   10  12  14  > ... <68  70  72  74  > ... <80  82  84  86  88  90  > ... <152 154 156 158 | <- bytes
   * +-------------------------------->     <---------------->     <------------------------>     <----------------+
   * |g0  g0  g1  g1  g2  g2  g3  g3  >     <g17 g17 i0  i0  >     <i2  i2  i3  i3  o0  o0  >     <o16 o16 o17 o17 | <- pixel
   *       /                              /                              /
   *      /                              /                              /
   *        /                              /                              /
   * +---------- gradientAccum (18) ---------+      +--- innerAccum (22) ---+      +--- outerAccum (40) ---+
   *
   *
   * Ultimately we do need the pixel's memory buffer index to set the output
   * pixel color, but it's faster to reconstruct the memory buffer location
   * each iteration of the final gradient calculation than it is to deconstruct
   * a memory location into x,y pairs each round.
   */
  /* clang-format on */

  gradientFillOffset = 0;  // since there are likely "more" of these, put it first. :)
  *innerEdgeOffset = gradientFillOffset + gsz;     // set start of inner edge indexes
  *outerEdgeOffset = (*innerEdgeOffset) + isz;     // set start of outer edge indexes
  /* set the accumulators to correct positions */  // set up some accumulator variables for loops
  gradientAccum = gradientFillOffset;  // each accumulator variable starts at its respective
  innerAccum = *innerEdgeOffset;       // section's offset so when we start filling, each
  outerAccum = *outerEdgeOffset;       // section fills up it's allocated space in gbuf
  // uses dmin=row, rsl=col
  for (x = 0, dmin = 0; x < t; x += rw, dmin++) {
    for (rsl = 0; rsl < rw; rsl++) {
      a = x + rsl;
      if (lres[a] == 2) {         // it is a gradient pixel flagged by 2
        ud = gradientAccum << 1;  // double the index to reach correct unsigned short location
        gbuf[ud] = dmin;          // insert pixel's row into gradient pixel location buffer
        gbuf[ud + 1] = rsl;       // insert pixel's column into gradient pixel location buffer
        gradientAccum++;          // increment gradient index buffer pointer
      }
      else if (lres[a] == 3) {  // it is an outer edge pixel flagged by 3
        ud = outerAccum << 1;   // double the index to reach correct unsigned short location
        gbuf[ud] = dmin;        // insert pixel's row into outer edge pixel location buffer
        gbuf[ud + 1] = rsl;     // insert pixel's column into outer edge pixel location buffer
        outerAccum++;           // increment outer edge index buffer pointer
        res[a] = 0.0f;          // set output pixel intensity now since it won't change later
      }
      else if (lres[a] == 4) {  // it is an inner edge pixel flagged by 4
        ud = innerAccum << 1;   // double int index to reach correct unsigned short location
        gbuf[ud] = dmin;        // insert pixel's row into inner edge pixel location buffer
        gbuf[ud + 1] = rsl;     // insert pixel's column into inner edge pixel location buffer
        innerAccum++;           // increment inner edge index buffer pointer
        res[a] = 1.0f;          // set output pixel intensity now since it won't change later
      }
    }
  }
}

static void do_fillGradientBuffer(unsigned int rw,
                                  float *res,
                                  unsigned short *gbuf,
                                  unsigned int isz,
                                  unsigned int osz,
                                  unsigned int gsz,
                                  unsigned int innerEdgeOffset,
                                  unsigned int outerEdgeOffset)
{
  int x;                     // x = pixel loop counter
  int a;                     // a = temporary pixel index buffer loop counter
  int fsz;                   // size of the frame
  unsigned int rsl;          // long used for finding fast 1.0/sqrt
  float rsf;                 // float used for finding fast 1.0/sqrt
  const float rsopf = 1.5f;  // constant float used for finding fast 1.0/sqrt

  unsigned int gradientFillOffset;
  unsigned int t;
  unsigned int ud;    // ud = unscaled edge distance
  unsigned int dmin;  // dmin = minimum edge distance
  float odist;        // odist = current outer edge distance
  float idist;        // idist = current inner edge distance
  int dx;             // dx = X-delta (used for distance proportion calculation)
  int dy;             // dy = Y-delta (used for distance proportion calculation)

  /*
   * The general algorithm used to color each gradient pixel is:
   *
   * 1.) Loop through all gradient pixels.
   * A.) For each gradient pixel:
   * a.) Loop though all outside edge pixels, looking for closest one
   * to the gradient pixel we are in.
   * b.) Loop through all inside edge pixels, looking for closest one
   * to the gradient pixel we are in.
   * c.) Find proportion of distance from gradient pixel to inside edge
   * pixel compared to sum of distance to inside edge and distance to
   * outside edge.
   *
   * In an image where:
   * . = blank (black) pixels, not covered by inner mask or outer mask
   * + = desired gradient pixels, covered only by outer mask
   * * = white full mask pixels, covered by at least inner mask
   *
   * ...............................
   * ...............+++++++++++.....
   * ...+O++++++..++++++++++++++....
   * ..+++\++++++++++++++++++++.....
   * .+++++G+++++++++*******+++.....
   * .+++++|+++++++*********+++.....
   * .++***I****************+++.....
   * .++*******************+++......
   * .+++*****************+++.......
   * ..+++***************+++........
   * ....+++**********+++...........
   * ......++++++++++++.............
   * ...............................
   *
   * O = outside edge pixel
   * \
   *  G = gradient pixel
   *  |
   *  I = inside edge pixel
   *
   *   __
   *  *note that IO does not need to be a straight line, in fact
   *  many cases can arise where straight lines do not work
   *  correctly.
   *
   *     __       __     __
   * d.) Pixel color is assigned as |GO| / ( |GI| + |GO| )
   *
   * The implementation does not compute distance, but the reciprocal of the
   * distance. This is done to avoid having to compute a square root, as a
   * reciprocal square root can be computed faster. Therefore, the code computes
   * pixel color as |GI| / (|GI| + |GO|). Since these are reciprocals, GI serves the
   * purpose of GO for the proportion calculation.
   *
   * For the purposes of the minimum distance comparisons, we only check
   * the sums-of-squares against eachother, since they are in the same
   * mathematical sort-order as if we did go ahead and take square roots
   *
   * Loop through all gradient pixels.
   */

  for (x = gsz - 1; x >= 0; x--) {
    gradientFillOffset = x << 1;
    t = gbuf[gradientFillOffset];        // calculate column of pixel indexed by gbuf[x]
    fsz = gbuf[gradientFillOffset + 1];  // calculate row of pixel indexed by gbuf[x]
    dmin = 0xffffffff;                   // reset min distance to edge pixel
    for (a = outerEdgeOffset + osz - 1; a >= outerEdgeOffset;
         a--) {  // loop through all outer edge buffer pixels
      ud = a << 1;
      dy = t - gbuf[ud];        // set dx to gradient pixel column - outer edge pixel row
      dx = fsz - gbuf[ud + 1];  // set dy to gradient pixel row - outer edge pixel column
      ud = dx * dx + dy * dy;   // compute sum of squares
      if (ud < dmin) {          // if our new sum of squares is less than the current minimum
        dmin = ud;              // set a new minimum equal to the new lower value
      }
    }
    odist = (float)(dmin);          // cast outer min to a float
    rsf = odist * 0.5f;             //
    rsl = *(unsigned int *)&odist;  // use some peculiar properties of the way bits are stored
    rsl = 0x5f3759df - (rsl >> 1);  // in floats vs. unsigned ints to compute an approximate
    odist = *(float *)&rsl;         // reciprocal square root
    odist = odist * (rsopf - (rsf * odist *
                              odist));  // -- ** this line can be iterated for more accuracy ** --
    dmin = 0xffffffff;                  // reset min distance to edge pixel
    for (a = innerEdgeOffset + isz - 1; a >= innerEdgeOffset;
         a--) {  // loop through all inside edge pixels
      ud = a << 1;
      dy = t - gbuf[ud];        // compute delta in Y from gradient pixel to inside edge pixel
      dx = fsz - gbuf[ud + 1];  // compute delta in X from gradient pixel to inside edge pixel
      ud = dx * dx + dy * dy;   // compute sum of squares
      if (ud < dmin) {  // if our new sum of squares is less than the current minimum we've found
        dmin = ud;      // set a new minimum equal to the new lower value
      }
    }
    idist = (float)(dmin);                            // cast inner min to a float
    rsf = idist * 0.5f;                               //
    rsl = *(unsigned int *)&idist;                    //
    rsl = 0x5f3759df - (rsl >> 1);                    // see notes above
    idist = *(float *)&rsl;                           //
    idist = idist * (rsopf - (rsf * idist * idist));  //
    /*
     * Note once again that since we are using reciprocals of distance values our
     * proportion is already the correct intensity, and does not need to be
     * subtracted from 1.0 like it would have if we used real distances.
     */

    /*
     * Here we reconstruct the pixel's memory location in the CompBuf by
     * Pixel Index = Pixel Column + ( Pixel Row * Row Width )
     */
    res[gbuf[gradientFillOffset + 1] + (gbuf[gradientFillOffset] * rw)] =
        (idist / (idist + odist));  // set intensity
  }
}

// end of copy

void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float *res)
{
  unsigned int *lres;    // lres = unsigned int pointer to output pixel buffer (for bit operations)
  unsigned int *limask;  // limask = unsigned int pointer to inner mask (for bit operations)
  unsigned int *lomask;  // lomask = unsigned int pointer to outer mask (for bit operations)

  int rw;   // rw = pixel row width
  int t;    // t = total number of pixels in buffer - 1 (used for loop starts)
  int fsz;  // size of the frame

  unsigned int isz = 0;   // size (in pixels) of inside edge pixel index buffer
  unsigned int osz = 0;   // size (in pixels) of outside edge pixel index buffer
  unsigned int gsz = 0;   // size (in pixels) of gradient pixel index buffer
  unsigned int rsize[3];  // size storage to pass to helper functions
  unsigned int innerEdgeOffset =
      0;  // offset into final buffer where inner edge pixel indexes start
  unsigned int outerEdgeOffset =
      0;  // offset into final buffer where outer edge pixel indexes start

  unsigned short *gbuf;  // gradient/inner/outer pixel location index buffer

  if (true) {  // if both input sockets have some data coming in...

    rw = this->getWidth();             // width of a row of pixels
    t = (rw * this->getHeight()) - 1;  // determine size of the frame
    memset(res,
           0,
           sizeof(float) * (t + 1));  // clear output buffer (not all pixels will be written later)

    lres = (unsigned int *)res;      // unsigned int pointer to output buffer (for bit level ops)
    limask = (unsigned int *)imask;  // unsigned int pointer to input mask (for bit level ops)
    lomask = (unsigned int *)omask;  // unsigned int pointer to output mask (for bit level ops)

    /*
     * The whole buffer is broken up into 4 parts. The four CORNERS, the FIRST and LAST rows, the
     * LEFT and RIGHT edges (excluding the corner pixels), and all OTHER rows.
     * This allows for quick computation of outer edge pixels where
     * a screen edge pixel is marked to be gradient.
     *
     * The pixel type (gradient vs inner-edge vs outer-edge) tests change
     * depending on the user selected "Inner Edge Mode" and the user selected
     * "Buffer Edge Mode" on the node's GUI. There are 4 sets of basically the
     * same algorithm:
     *
     * 1.) Inner Edge -> Adjacent Only
     *   Buffer Edge -> Keep Inside
     *
     * 2.) Inner Edge -> Adjacent Only
     *   Buffer Edge -> Bleed Out
     *
     * 3.) Inner Edge -> All
     *   Buffer Edge -> Keep Inside
     *
     * 4.) Inner Edge -> All
     *   Buffer Edge -> Bleed Out
     *
     * Each version has slightly different criteria for detecting an edge pixel.
     */
    if (this->m_adjecentOnly) {  // if "adjacent only" inner edge mode is turned on
      if (this->m_keepInside) {  // if "keep inside" buffer edge mode is turned on
        do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize);
      }
      else {  // "bleed out" buffer edge mode is turned on
        do_adjacentBleedBorders(t, rw, limask, lomask, lres, res, rsize);
      }
      // set up inner edge, outer edge, and gradient buffer sizes after border pass
      isz = rsize[0];
      osz = rsize[1];
      gsz = rsize[2];
      // detect edges in all non-border pixels in the buffer
      do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz);
    }
    else {                       // "all" inner edge mode is turned on
      if (this->m_keepInside) {  // if "keep inside" buffer edge mode is turned on
        do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize);
      }
      else {  // "bleed out" buffer edge mode is turned on
        do_allBleedBorders(t, rw, limask, lomask, lres, res, rsize);
      }
      // set up inner edge, outer edge, and gradient buffer sizes after border pass
      isz = rsize[0];
      osz = rsize[1];
      gsz = rsize[2];
      // detect edges in all non-border pixels in the buffer
      do_allEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz);
    }

    // set edge and gradient buffer sizes once again...
    // the sizes in rsize[] may have been modified
    // by the do_*EdgeDetection() function.
    isz = rsize[0];
    osz = rsize[1];
    gsz = rsize[2];

    // calculate size of pixel index buffer needed
    fsz = gsz + isz + osz;
    // allocate edge/gradient pixel index buffer
    gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM");

    do_createEdgeLocationBuffer(
        t, rw, lres, res, gbuf, &innerEdgeOffset, &outerEdgeOffset, isz, gsz);
    do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, innerEdgeOffset, outerEdgeOffset);

    // free the gradient index buffer
    MEM_freeN(gbuf);
  }
}

DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() : NodeOperation()
{
  this->addInputSocket(COM_DT_VALUE);
  this->addInputSocket(COM_DT_VALUE);
  this->addOutputSocket(COM_DT_VALUE);
  this->m_inputInnerMask = NULL;
  this->m_inputOuterMask = NULL;
  this->m_adjecentOnly = false;
  this->m_keepInside = false;
  this->setComplex(true);
}

bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/,
                                                               ReadBufferOperation *readOperation,
                                                               rcti *output)
{
  if (this->m_cachedInstance == NULL) {
    rcti newInput;
    newInput.xmax = this->getWidth();
    newInput.xmin = 0;
    newInput.ymax = this->getHeight();
    newInput.ymin = 0;
    return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output);
  }
  else {
    return false;
  }
}

void DoubleEdgeMaskOperation::initExecution()
{
  this->m_inputInnerMask = this->getInputSocketReader(0);
  this->m_inputOuterMask = this->getInputSocketReader(1);
  initMutex();
  this->m_cachedInstance = NULL;
}

void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect)
{
  if (this->m_cachedInstance) {
    return this->m_cachedInstance;
  }

  lockMutex();
  if (this->m_cachedInstance == NULL) {
    MemoryBuffer *innerMask = (MemoryBuffer *)this->m_inputInnerMask->initializeTileData(rect);
    MemoryBuffer *outerMask = (MemoryBuffer *)this->m_inputOuterMask->initializeTileData(rect);
    float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(),
                                       __func__);
    float *imask = innerMask->getBuffer();
    float *omask = outerMask->getBuffer();
    doDoubleEdgeMask(imask, omask, data);
    this->m_cachedInstance = data;
  }
  unlockMutex();
  return this->m_cachedInstance;
}
void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void *data)
{
  float *buffer = (float *)data;
  int index = (y * this->getWidth() + x);
  output[0] = buffer[index];
}

void DoubleEdgeMaskOperation::deinitExecution()
{
  this->m_inputInnerMask = NULL;
  this->m_inputOuterMask = NULL;
  deinitMutex();
  if (this->m_cachedInstance) {
    MEM_freeN(this->m_cachedInstance);
    this->m_cachedInstance = NULL;
  }
}
